using System;
using System.Collections.Generic;
using System.Text;
using Antlr.Runtime.Tree;
using SimpleSpreadsheet.Core.Parser;

namespace SimpleSpreadsheet.Core.Expressions
{
    /// <summary>
    /// Basic type of expression that can be extracted from parsed expression text
    /// </summary>
    public abstract class BaseExpression
    {
        /// <summary>
        /// Creates the necessary expression type using provided AST
        /// </summary>
        /// <param name="ast"></param>
        /// <param name="referenceRegistrator"></param>
        /// <returns></returns>
        public static BaseExpression Create(CommonTree ast, Action<ReferenceExpression> referenceRegistrator)
        {
            if (ast == null)
                throw new ArgumentNullException("ast");

            switch (ast.Type)
            {
                case ExpressionParser.INTEGER:
                    return new ValueExpression(ast.Text, ValueExpressionType.Integer);

                case ExpressionParser.FLOAT:
                    return new ValueExpression(ast.Text, ValueExpressionType.Float);

                case ExpressionParser.NOT:
                    return new UnaryExpression(UnaryExpressionType.Not,
                                               Create((CommonTree) ast.GetChild(0), referenceRegistrator));

                case ExpressionParser.NEGATE:
                    return new UnaryExpression(UnaryExpressionType.Negate,
                                               Create((CommonTree) ast.GetChild(0), referenceRegistrator));

                case ExpressionParser.MULT:
                    return new BinaryExpresssion(BinaryExpressionType.Times,
                                                 Create((CommonTree) ast.GetChild(0), referenceRegistrator),
                                                 Create((CommonTree) ast.GetChild(1), referenceRegistrator));

                case ExpressionParser.POW:
                    return new BinaryExpresssion(BinaryExpressionType.Pow,
                                                 Create((CommonTree) ast.GetChild(0), referenceRegistrator),
                                                 Create((CommonTree) ast.GetChild(1), referenceRegistrator));

                case ExpressionParser.DIV:
                    return new BinaryExpresssion(BinaryExpressionType.Div,
                                                 Create((CommonTree) ast.GetChild(0), referenceRegistrator),
                                                 Create((CommonTree) ast.GetChild(1), referenceRegistrator));

                case ExpressionParser.MOD:
                    return new BinaryExpresssion(BinaryExpressionType.Modulo,
                                                 Create((CommonTree) ast.GetChild(0), referenceRegistrator),
                                                 Create((CommonTree) ast.GetChild(1), referenceRegistrator));

                case ExpressionParser.PLUS:
                    return new BinaryExpresssion(BinaryExpressionType.Plus,
                                                 Create((CommonTree) ast.GetChild(0), referenceRegistrator),
                                                 Create((CommonTree) ast.GetChild(1), referenceRegistrator));

                case ExpressionParser.MINUS:
                    return new BinaryExpresssion(BinaryExpressionType.Minus,
                                                 Create((CommonTree) ast.GetChild(0), referenceRegistrator),
                                                 Create((CommonTree) ast.GetChild(1), referenceRegistrator));

                case ExpressionParser.LT:
                    return new BinaryExpresssion(BinaryExpressionType.Lesser,
                                                 Create((CommonTree) ast.GetChild(0), referenceRegistrator),
                                                 Create((CommonTree) ast.GetChild(1), referenceRegistrator));

                case ExpressionParser.LTEQ:
                    return new BinaryExpresssion(BinaryExpressionType.LesserOrEqual,
                                                 Create((CommonTree) ast.GetChild(0), referenceRegistrator),
                                                 Create((CommonTree) ast.GetChild(1), referenceRegistrator));

                case ExpressionParser.GT:
                    return new BinaryExpresssion(BinaryExpressionType.Greater,
                                                 Create((CommonTree) ast.GetChild(0), referenceRegistrator),
                                                 Create((CommonTree) ast.GetChild(1), referenceRegistrator));

                case ExpressionParser.GTEQ:
                    return new BinaryExpresssion(BinaryExpressionType.GreaterOrEqual,
                                                 Create((CommonTree) ast.GetChild(0), referenceRegistrator),
                                                 Create((CommonTree) ast.GetChild(1), referenceRegistrator));

                case ExpressionParser.EQUALS:
                    return new BinaryExpresssion(BinaryExpressionType.Equal,
                                                 Create((CommonTree) ast.GetChild(0), referenceRegistrator),
                                                 Create((CommonTree) ast.GetChild(1), referenceRegistrator));

                case ExpressionParser.NOTEQUALS:
                    return new BinaryExpresssion(BinaryExpressionType.NotEqual,
                                                 Create((CommonTree) ast.GetChild(0), referenceRegistrator),
                                                 Create((CommonTree) ast.GetChild(1), referenceRegistrator));

                case ExpressionParser.AND:
                    return new BinaryExpresssion(BinaryExpressionType.And,
                                                 Create((CommonTree) ast.GetChild(0), referenceRegistrator),
                                                 Create((CommonTree) ast.GetChild(1), referenceRegistrator));

                case ExpressionParser.OR:
                    return new BinaryExpresssion(BinaryExpressionType.Or,
                                                 Create((CommonTree) ast.GetChild(0), referenceRegistrator),
                                                 Create((CommonTree) ast.GetChild(1), referenceRegistrator));

                case ExpressionLexer.FUNCTION:
                    var expressions = new List<BaseExpression>();

                    for (int i = 0; i < ast.ChildCount; i++)
                    {
                        BaseExpression expr = Create((CommonTree) ast.GetChild(i), referenceRegistrator);
                        var rr = expr as ReferenceRangeExpression;
                        if (rr != null)
                            expressions.AddRange(rr.References);
                        else
                            expressions.Add(expr);
                    }
                    return new FunctionExpression(ast.Text, expressions);

                case ExpressionLexer.REFERENCE:
                    var p = new ReferenceExpression(((CommonTree) ast.GetChild(0)).Text);
                    referenceRegistrator(p);
                    return p;

                case ExpressionLexer.RANGEREFERENCE:
                    var rangeExpression = new ReferenceRangeExpression(((CommonTree) ast.GetChild(0)).Text);
                    rangeExpression.References.ForEach(referenceRegistrator);
                    return rangeExpression;

                case ExpressionLexer.PARAM:
                    var par = new ParameterExpression(((CommonTree) ast.GetChild(0)).Text);
                    return par;

                default:
                    return null;
            }
        }

        /// <summary>
        /// Accepts visitor to perform evaluation
        /// </summary>
        /// <param name="visitor"></param>
        public virtual void Accept(ExpressionVisitor visitor)
        {
            visitor.Visit(this);
        }
    }
}